
using System;
using System.ComponentModel;
using System.Globalization;
using System.Resources;
using System.Reflection;


namespace MVCFramework
    {

        #region GlobalizedPropertyDescriptor

        /// <summary>
        /// GlobalizedPropertyDescriptor enhances the base class bay obtaining the display name for a property
        /// from the resource.
        /// </summary>
        public class GlobalizedPropertyDescriptor : PropertyDescriptor
        {
            private PropertyDescriptor basePropertyDescriptor;
            private String localizedName = "";
            private String localizedDescription = "";

            public GlobalizedPropertyDescriptor(PropertyDescriptor basePropertyDescriptor)
                : base(basePropertyDescriptor)
            {
                this.basePropertyDescriptor = basePropertyDescriptor;
            }

            public override AttributeCollection Attributes
            {
                get
                {
                    AttributeCollection attrs = basePropertyDescriptor.Attributes;
                    foreach (Attribute attr in attrs)
                    {
                        if (attr is GlobalizedCategoryAttribute)
                        {
                            GlobalizedCategoryAttribute attrCat = (GlobalizedCategoryAttribute)attr;
                            attrCat.TableName = basePropertyDescriptor.ComponentType.Namespace + "." + basePropertyDescriptor.ComponentType.Name;
                        }
                    }
                    return attrs;
                }
            }
            
            public override bool CanResetValue(object component)
            {
                return basePropertyDescriptor.CanResetValue(component);
            }

            public override Type ComponentType
            {
                get { return basePropertyDescriptor.ComponentType; }
            }

            public override string DisplayName
            {
                get
                {
                    // First lookup the property if GlobalizedPropertyAttribute instances are available. 
                    // If yes, then try to get resource table name and display name id from that attribute.
                    string tableName = "";
                    string displayName = "";
                    foreach (Attribute oAttrib in this.basePropertyDescriptor.Attributes)
                    {
                        if (oAttrib.GetType().Equals(typeof(GlobalizedPropertyAttribute)))
                        {
                            displayName = ((GlobalizedPropertyAttribute)oAttrib).Name;
                            tableName = ((GlobalizedPropertyAttribute)oAttrib).Table;
                        }
                    }
                    // If no resource table is specified at the property level, then look if it is done
                    // on the class level
                    if (tableName.Length == 0)
                    {
                        GlobalizedObjectAttribute[] attrs = (GlobalizedObjectAttribute[])basePropertyDescriptor.ComponentType.GetCustomAttributes(typeof(GlobalizedObjectAttribute), false);
                        if (attrs.Length > 0)
                        {
                            tableName = ((GlobalizedObjectAttribute)attrs[0]).Table;
                        }
                    }
                    // If still no resource table specified by attribute, then build it itself by using namespace and class name.
                    if (tableName.Length == 0)
                        tableName = basePropertyDescriptor.ComponentType.Namespace + "." + basePropertyDescriptor.ComponentType.Name;

                    // If no display name id is specified by attribute, then construct it by using default display name (usually the property name) 
                    if (displayName.Length == 0)
                        displayName = this.basePropertyDescriptor.DisplayName;

                    // Now use table name and display name id to access the resources.  
                    ResourceManager rm = new ResourceManager(tableName, basePropertyDescriptor.ComponentType.Assembly);
                    string s = null ;
                    // Get the string from the resources. 
                    // If this fails, then use default display name (usually the property name) 
                    try
                    {
                       s  = rm.GetString(displayName);
                    }
                    catch {}
                    this.localizedName = (s != null) ? s : this.basePropertyDescriptor.DisplayName;

                    return this.localizedName;
                }
            }

            public override string Description
            {
                get
                {
                    // First lookup the property if there are GlobalizedPropertyAttribute instances
                    // are available. 
                    // If yes, try to get resource table name and display name id from that attribute.
                    string tableName = "";
                    string displayName = "";
                    foreach (Attribute oAttrib in this.basePropertyDescriptor.Attributes)
                    {
                        if (oAttrib.GetType().Equals(typeof(GlobalizedPropertyAttribute)))
                        {
                            displayName = ((GlobalizedPropertyAttribute)oAttrib).Description;
                            tableName = ((GlobalizedPropertyAttribute)oAttrib).Table;
                        }
                    }

                    // If no resource table is specified at the property level, then look if it is done
                    // on the class level
                    if (tableName.Length == 0)
                    {
                        GlobalizedObjectAttribute[] attrs = (GlobalizedObjectAttribute[])basePropertyDescriptor.ComponentType.GetCustomAttributes(typeof(GlobalizedObjectAttribute), false);
                        if (attrs.Length > 0)
                        {
                            tableName = ((GlobalizedObjectAttribute)attrs[0]).Table;
                        }
                    }

                    // If no resource table specified by attribute, then build it itself by using namespace and class name.
                    if (tableName.Length == 0)
                        tableName = basePropertyDescriptor.ComponentType.Namespace + "." + basePropertyDescriptor.ComponentType.Name;

                    // If no display name id is specified by attribute, then construct it by using default display name (usually the property name) 
                    if (displayName.Length == 0)
                        displayName = this.basePropertyDescriptor.DisplayName + "Description";

                    // Now use table name and display name id to access the resources.  
                    ResourceManager rm = new ResourceManager(tableName, basePropertyDescriptor.ComponentType.Assembly);

                      string s = null ;
                    // Get the string from the resources. 
                    // If this fails, then use default display name (usually the property name) 
                      try
                      {
                          s = rm.GetString(displayName);
                      }
                      catch { }
                    this.localizedDescription = (s != null) ? s : "";

                    return this.localizedDescription;
                }
            }

            public override object GetValue(object component)
            {
                return this.basePropertyDescriptor.GetValue(component);
            }

            public override bool IsReadOnly
            {
                get { return this.basePropertyDescriptor.IsReadOnly; }
            }

            public override string Name
            {
                get { return this.basePropertyDescriptor.Name; }
            }

            public override Type PropertyType
            {
                get { return this.basePropertyDescriptor.PropertyType; }
            }

            public override void ResetValue(object component)
            {
                this.basePropertyDescriptor.ResetValue(component);
            }

            public override bool ShouldSerializeValue(object component)
            {
                return this.basePropertyDescriptor.ShouldSerializeValue(component);
            }

            public override void SetValue(object component, object value)
            {
                this.basePropertyDescriptor.SetValue(component, value);
            }
        }
        #endregion

        #region GlobalizedObject

        /// <summary>
        /// GlobalizedObject implements ICustomTypeDescriptor to enable 
        /// required functionality to describe a type (class).<br></br>
        /// The main task of this class is to instantiate our own property descriptor 
        /// of type GlobalizedPropertyDescriptor.  
        /// </summary>
        public class GlobalizedObject : ICustomTypeDescriptor
        {
            private PropertyDescriptorCollection globalizedProps;

            public String GetClassName()
            {
                return TypeDescriptor.GetClassName(this, true);
            }

            public AttributeCollection GetAttributes()
            {
                return TypeDescriptor.GetAttributes(this, true);
            }

            public String GetComponentName()
            {
                return TypeDescriptor.GetComponentName(this, true);
            }

            public TypeConverter GetConverter()
            {
                return TypeDescriptor.GetConverter(this, true);
            }

            public EventDescriptor GetDefaultEvent()
            {
                return TypeDescriptor.GetDefaultEvent(this, true);
            }

            public PropertyDescriptor GetDefaultProperty()
            {
                return TypeDescriptor.GetDefaultProperty(this, true);
            }

            public object GetEditor(Type editorBaseType)
            {
                return TypeDescriptor.GetEditor(this, editorBaseType, true);
            }

            public EventDescriptorCollection GetEvents(Attribute[] attributes)
            {
                return TypeDescriptor.GetEvents(this, attributes, true);
            }

            public EventDescriptorCollection GetEvents()
            {
                return TypeDescriptor.GetEvents(this, true);
            }

            /// <summary>
            /// Called to get the properties of a type.
            /// </summary>
            /// <param name="attributes"></param>
            /// <returns></returns>
            public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
            {
                if (globalizedProps == null)
                {
                    // Get the collection of properties
                    PropertyDescriptorCollection baseProps = TypeDescriptor.GetProperties(this, attributes, true);

                    globalizedProps = new PropertyDescriptorCollection(null);

                    // For each property use a property descriptor of our own that is able to be globalized
                    foreach (PropertyDescriptor oProp in baseProps)
                    {
                        globalizedProps.Add( ( new GlobalizedPropertyDescriptor(oProp)));
                    }
                }
                return globalizedProps;
            }

            public PropertyDescriptorCollection GetProperties()
            {
                // Only do once
                if (globalizedProps == null)
                {
                    // Get the collection of properties
                    PropertyDescriptorCollection baseProps = TypeDescriptor.GetProperties(this, true);
                    globalizedProps = new PropertyDescriptorCollection(null);

                    // For each property use a property descriptor of our own that is able to be globalized
                    foreach (PropertyDescriptor oProp in baseProps)
                    {
                        globalizedProps.Add( ( new GlobalizedPropertyDescriptor(oProp)));
                    }
                }
                return globalizedProps;
            }

            public object GetPropertyOwner(PropertyDescriptor pd)
            {
                return this;
            }
        }

        #endregion

        // This attribute may be added to properties to control the resource table name and 
        // keys for accesging the property name and description. 
        [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
        public class GlobalizedPropertyAttribute : Attribute
        {
            private String resourceName = "";
            private String resourceDescription = "";
            private String resourceTable = "";

            public GlobalizedPropertyAttribute(String name)
            {
                resourceName = name;
            }

            // The key for a property name into the resource table.
            // If empty the property name will be used by default.
            public String Name
            {
                get { return resourceName; }
                set { resourceName = value; }
            }

            // The key for a property description into the resource table.
            // If empty the property name appended by 'Description' will be used by default.
            public String Description
            {
                get { return resourceDescription; }
                set { resourceDescription = value; }
            }

            // The name fully qualified name of the resource table to use, f.ex.
            // "GlobalizedPropertyGrid.Person".
            public String Table
            {
                get { return resourceTable; }
                set { resourceTable = value; }
            }
        }

        // This attribute may be added to properties to control the resource table name and 
        // keys for accesging the property name and description. 
        [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
        public class GlobalizedObjectAttribute : Attribute
        {
            private String resourceTable = "";

            public GlobalizedObjectAttribute(String name)
            {
                resourceTable = name;
            }

            // resource table name to use for the class. 
            // If this string is empty the name of the class will be used by default. 
            public String Table
            {
                get { return resourceTable; }
            }
        }

        // This attribute class extends the CategoryAttribute class from the .NET framework
        // to support localization.
        [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
        class GlobalizedCategoryAttribute : CategoryAttribute
        {
            string table = "";

            public GlobalizedCategoryAttribute() : base() { }
            public GlobalizedCategoryAttribute(string name) : base(name) { }

            // The name fully qualified name of the resource table to use, f.ex.
            // "GlobalizedPropertyGrid.Person".
            public string TableName
            {
                set { table = value; }
                get { return table; }
            }

            // This method will be called by the component framework to get a localized
            // string. Note: Only called once on first access.
            protected override string GetLocalizedString(string value)
            {
                string baseStr = base.GetLocalizedString(value);

                // Now use table name and display name id to access the resources.  
                ResourceManager rm = new ResourceManager(table, Assembly.GetExecutingAssembly());

                // Get the string from the resources.
                try
                {
                    return rm.GetString(value);
                }
                catch (Exception)
                {
                    return value;
                }
            }
        }

}


